//
//  TTOSSService.swift
//  TTService
//
//  Created by Toj on 4/13/21.
//
// oss开发文档: https://help.aliyun.com/document_detail/32055.html?spm=a2c4g.11186623.6.1437.11e437957Q8F6y

import Foundation
import AliyunOSSiOS

public typealias TTOSSUploadProgressBlock = ((_ bytesSent: Int64, _ totalByteSent: Int64, _ totalBytesExpectedToSend: Int64) -> Void)
public typealias TTOSSUploadCompletedBlock = ((_ error: Error?) -> Void)

public typealias TTOSSDownloadProgressBlock = ((_ bytesWritten: Int64, _ totalBytesWritten: Int64, _ totalBytesExpectedToWrite: Int64) -> Void)
public typealias TTOSSDownloadCompletedBlock = ((_ error: Error?, _ data: Data?) -> Void)

open class TTOSSService: NSObject {
    private var credential: OSSCredentialProvider!
    private var client: OSSClient!
    private lazy var configuration = OSSClientConfiguration()
    
    public init(endpoint: String, serverUrl: String) {
        super.init()
        
        credential = OSSAuthCredentialProvider(authServerUrl: serverUrl)
        
        setupMakeClient(endpoint: endpoint, credential: credential)
    }
    
    public init(endpoint: String, accessKey: String, secretKey: String, token: String, expiration: String) {
        super.init()
        
        credential = OSSAuthCredentialProvider { () ->  OSSFederationToken? in
            let federationToken = OSSFederationToken()
            federationToken.tAccessKey = accessKey
            federationToken.tSecretKey = secretKey
            federationToken.tToken = token
            federationToken.expirationTimeInGMTFormat = expiration
            return federationToken
        }
        
        setupMakeClient(endpoint: endpoint, credential: credential)
    }
    
    private func setupMakeClient(endpoint: String, credential: OSSCredentialProvider) {
        
        client = OSSClient(endpoint: endpoint, credentialProvider: credential)
    }
}

// MARK: - upload
/** 内存中上传或本地上传文件. 最大不能超过5GB.
 bucketName: 存储空间名称
 objectKey == objectName: 表示将文件上传到OSS时需要指定包含文件后缀在内的完整路径
 例如: abc/efg/123.jpg.
 */
public extension TTOSSService {
    
    func upload(bucket name: String,
                object keyPath: String,
                from url: String = "",
                data: Data? = nil,
                progress: TTOSSUploadProgressBlock? = nil,
                completed: TTOSSUploadCompletedBlock? = nil) {
        let request = OSSPutObjectRequest()
        request.bucketName = name
        request.objectKey = keyPath
        
        if url.count > 0, let fileURL = URL(string: url) {
            request.uploadingFileURL = fileURL
        }
        else if let data = data, data.count > 0 {
            // 直接上传NSData
            request.uploadingData = data
        }
        
        if (progress != nil) {
            request.uploadProgress = { (bytesSent, totalByteSent, totalBytesExpectedToSend) -> Void in
                progress?(bytesSent, totalByteSent, totalBytesExpectedToSend)
            }
        }
        
        let putTask = client.putObject(request)
        putTask.continue({ (task) -> Any? in
            guard completed != nil else { return nil }
            completed?(task.error)
            return nil
        })
    }
}

// MARK: - download
/** 可以指定下载为本地文件或者下载为Data
 bucketName: 存储空间名称
 objectKey == objectName: 表示将文件上传到OSS时需要指定包含文件后缀在内的完整路径
 例如: abc/efg/123.jpg.
 */
public extension TTOSSService {
    
    func download(bucket name: String,
                  object keyPath: String,
                  to url: String = "",
                  progress: TTOSSDownloadProgressBlock? = nil,
                  completed: TTOSSDownloadCompletedBlock? = nil) {
        
        let request = OSSGetObjectRequest()
        request.bucketName = name
        request.objectKey = keyPath
        request.downloadToFileURL = URL(fileURLWithPath: url)
        
//        if url.count > 0, let fileURL = URL(string: url) {
//            request.downloadToFileURL = fileURL
//        }
        
        if (progress != nil) {
            request.downloadProgress = { (bytesWritten, totalBytesWritten, totalBytesExpectedToWrite) -> Void in
                progress?(bytesWritten, totalBytesWritten, totalBytesExpectedToWrite)
            }
        }
        
        let getTask = client.getObject(request)
        getTask.continue({ (task) -> Any? in
            guard completed != nil else { return nil }
            var data: Data?
            if let result = task.result {
                data = result.downloadedData
            }
            completed?(task.error, data)
            return nil
        })
    }
}
